home *** CD-ROM | disk | FTP | other *** search
/ Visual Cafe 3 / Visual Cafe 3.ISO / Vcafe / JFC.bin / BasicTextUI.java < prev    next >
Text File  |  1998-06-30  |  40KB  |  1,160 lines

  1. /*
  2.  * @(#)BasicTextUI.java    1.10 98/04/09
  3.  *
  4.  * Copyright (c) 1997 Sun Microsystems, Inc. All Rights Reserved.
  5.  *
  6.  * This software is the confidential and proprietary information of Sun
  7.  * Microsystems, Inc. ("Confidential Information").  You shall not
  8.  * disclose such Confidential Information and shall use it only in
  9.  * accordance with the terms of the license agreement you entered into
  10.  * with Sun.
  11.  *
  12.  * SUN MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF THE
  13.  * SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
  14.  * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
  15.  * PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR ANY DAMAGES
  16.  * SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR DISTRIBUTING
  17.  * THIS SOFTWARE OR ITS DERIVATIVES.
  18.  *
  19.  */
  20. package com.sun.java.swing.plaf.basic;
  21.  
  22. import java.awt.*;
  23. import java.awt.event.*;
  24. import java.beans.*;
  25. import java.io.*;
  26. import com.sun.java.swing.*;
  27. import com.sun.java.swing.plaf.*;
  28. import com.sun.java.swing.text.*;
  29. import com.sun.java.swing.event.*;
  30. import com.sun.java.swing.border.Border;
  31.  
  32. /**
  33.  * <p>
  34.  * Basis of a text components look-and-feel.  This provides the
  35.  * basic editor view and controller services that may be useful
  36.  * when creating a look-and-feel for an extension of JTextComponent.
  37.  * <p>
  38.  * Most state is held in the associated JTextComponent as bound
  39.  * properties, and the UI installs default values for the 
  40.  * various properties.  This default will install something for
  41.  * all of the properties.  Typically, a LAF implementation will
  42.  * do more however.  At a minimum, a LAF would generally install
  43.  * key bindings.
  44.  * <p>
  45.  * An important method to define is the
  46.  * <a href="#getPropertyPrefix">getPropertyPrefix</a> method
  47.  * which is used as the basis of the keys used to fetch defaults
  48.  * from the UIManager.  The string should reflect the type of 
  49.  * TextUI (eg. TextField, TextArea, etc) without the particular 
  50.  * LAF part of the name (eg Metal, Motif, etc).
  51.  * <p>
  52.  * To build a view of the model, one of the following strategies 
  53.  * can be employed.
  54.  * <ol>
  55.  * <li>
  56.  * One strategy is to simply redefine the 
  57.  * ViewFactory interface in the UI.  By default, this UI itself acts
  58.  * as the factory for View implementations.  This is useful
  59.  * for simple factories.  To do this reimplement the 
  60.  * <a href="#create">create</a> method.
  61.  * <li>
  62.  * A common strategy for creating more complex types of documents
  63.  * is to have the EditorKit implementation return a factory.  Since
  64.  * the EditorKit ties all of the pieces necessary to maintain a type
  65.  * of document, the factory is typically an important part of that
  66.  * and should be produced by the EditorKit implementation.
  67.  * <li>
  68.  * A less common way to create more complex types is to have
  69.  * the UI implementation create a.
  70.  * seperate object for the factory.  To do this, the 
  71.  * <a href="#createViewFactory">createViewFactory</a>
  72.  * method should be reimplemented to return some factory.
  73.  * </ol>
  74.  * <p>
  75.  * Warning: serialized objects of this class will not be compatible with
  76.  * future swing releases.  The current serialization support is appropriate 
  77.  * for short term storage or RMI between Swing1.0 applications.  It will
  78.  * not be possible to load serialized Swing1.0 objects with future releases
  79.  * of Swing.  The JDK1.2 release of Swing will be the compatibility
  80.  * baseline for the serialized form of Swing objects.
  81.  *
  82.  * @author  Timothy Prinzing
  83.  * @version 1.10 04/09/98
  84.  */
  85. public abstract class BasicTextUI extends TextUI implements ViewFactory, Serializable {
  86.  
  87.     /**
  88.      * Creates a new UI.
  89.      */
  90.     public BasicTextUI() {
  91.         painted = false;
  92.     }
  93.  
  94.     /**
  95.      * Creates the object to use for a caret.  By default an
  96.      * instance of JCaret is created.  This method
  97.      * can be redefined to provide something else that implements
  98.      * the InputPosition interface or a subclass of JCaret.
  99.      *
  100.      * @return the caret object
  101.      */
  102.     protected Caret createCaret() {
  103.         return new DefaultCaret();
  104.     }
  105.  
  106.     /**
  107.      * Creates the object to use for adding highlights.  By default
  108.      * an instance of DefaultHighlighter is created.  This method
  109.      * can be redefined to provide something else that implements
  110.      * the Highlighter interface or a subclass of DefaultHighlighter.
  111.      *
  112.      * @return the highlighter
  113.      */
  114.     protected Highlighter createHighlighter() {
  115.         return new DefaultHighlighter();
  116.     }
  117.  
  118.     /**
  119.      * Fetches the name of the keymap that will be installed/used 
  120.      * by default for this UI. This is implemented to create a
  121.      * name based upon the classname.  The name is the the name
  122.      * of the class with the package prefix removed.
  123.      *
  124.      * @return the name
  125.      */
  126.     protected String getKeymapName() {
  127.     String nm = getClass().getName();
  128.     int index = nm.lastIndexOf('.');
  129.     if (index >= 0) {
  130.         nm = nm.substring(index+1, nm.length());
  131.     }
  132.     return nm;
  133.     }
  134.  
  135.     /**
  136.      * Creates the keymap to use for the text component, and installs
  137.      * any necessary bindings into it.  By default, the keymap is
  138.      * shared between all instances of this type of TextUI. The
  139.      * keymap has the name defined by the getKeymapName method.  If the
  140.      * keymap is not found, then DEFAULT_KEYMAP from JTextComponent is used.
  141.      * <p>
  142.      * The set of bindings used to create the keymap is fetched 
  143.      * from the UIManager using a key formed by combining the
  144.      * <a href="#getPropertyPrefix">getPropertyPrefix</a> method
  145.      * and the string <code>.keyBindings</code>.  The type is expected
  146.      * to be <code>JTextComponent.KeyBinding[]</code>.
  147.      *
  148.      * @return the keymap
  149.      * @see #getKeymapName
  150.      * @see com.sun.java.swing.text.JTextComponent
  151.      */
  152.     protected Keymap createKeymap() {
  153.     String nm = getKeymapName();
  154.     Keymap map = JTextComponent.getKeymap(nm);
  155.     if (map == null) {
  156.         Keymap parent = JTextComponent.getKeymap(JTextComponent.DEFAULT_KEYMAP);
  157.         map = JTextComponent.addKeymap(nm, parent);
  158.         String prefix = getPropertyPrefix();
  159.         Object o = UIManager.get(prefix + ".keyBindings");
  160.         if ((o != null) && (o instanceof JTextComponent.KeyBinding[])) {
  161.         JTextComponent.KeyBinding[] bindings = (JTextComponent.KeyBinding[]) o;
  162.         JTextComponent.loadKeymap(map, bindings, getComponent().getActions());
  163.         }
  164.     }
  165.     return map;
  166.     }
  167.  
  168.     /**
  169.      * This method gets called when a bound property is changed
  170.      * on the associated JTextComponent.  This is a hook
  171.      * which UI implementations may change to reflect how the
  172.      * UI displays bound properties of JTextComponent subclasses.
  173.      * This is implemented to do nothing (i.e. the response to
  174.      * properties in JTextComponent itself are handled prior
  175.      * to calling this method).
  176.      *
  177.      * @param evt the property change event
  178.      */
  179.     protected void propertyChange(PropertyChangeEvent evt) {
  180.     }
  181.  
  182.     /**
  183.      * Gets the name used as a key to look up properties through the
  184.      * UIManager.  This is used as a prefix to all the standard
  185.      * text properties.
  186.      *
  187.      * @return the name
  188.      */
  189.     protected abstract String getPropertyPrefix();
  190.  
  191.     /**
  192.      * Initializes component properties, e.g. font, foreground, 
  193.      * background, caret color, selection color, selected text color,
  194.      * disabled text color, and border color.  The font, foreground, and
  195.      * background properties are only set if their current value is either null
  196.      * or a UIResource, other properties are set if the current
  197.      * value is null.
  198.      * 
  199.      * @param c the editor component
  200.      * @see #uninstallDefaults
  201.      * @see #installUI
  202.      */
  203.     protected void installDefaults(JComponent c) 
  204.     {
  205.         String prefix = getPropertyPrefix();
  206.         Font f = editor.getFont();
  207.         if ((f == null) || (f instanceof UIResource)) {
  208.             editor.setFont(UIManager.getFont(prefix + ".font"));
  209.         }
  210.  
  211.         Color bg = editor.getBackground();
  212.         if ((bg == null) || (bg instanceof UIResource)) {
  213.             editor.setBackground(UIManager.getColor(prefix + ".background"));
  214.         }
  215.         
  216.         Color fg = editor.getForeground();
  217.         if ((fg == null) || (fg instanceof UIResource)) {
  218.             editor.setForeground(UIManager.getColor(prefix + ".foreground"));
  219.         }
  220.  
  221.         Color color = editor.getCaretColor();
  222.         if ((color == null) || (color instanceof UIResource)) {
  223.             editor.setCaretColor(UIManager.getColor(prefix + ".caretForeground"));
  224.         }
  225.  
  226.         Color s = editor.getSelectionColor();
  227.         if ((s == null) || (s instanceof UIResource)) {
  228.             editor.setSelectionColor(UIManager.getColor(prefix + ".selectionBackground"));
  229.         }
  230.  
  231.         Color sfg = editor.getSelectedTextColor();
  232.         if ((sfg == null) || (sfg instanceof UIResource)) {
  233.             editor.setSelectedTextColor(UIManager.getColor(prefix + ".selectionForeground"));
  234.         }
  235.  
  236.         Color dfg = editor.getDisabledTextColor();
  237.         if ((dfg == null) || (dfg instanceof UIResource)) {
  238.             editor.setDisabledTextColor(UIManager.getColor(prefix + ".inactiveForeground"));
  239.         }
  240.  
  241.         Border b = editor.getBorder();
  242.         if ((b == null) || (b instanceof UIResource)) {
  243.             editor.setBorder(UIManager.getBorder(prefix + ".border"));
  244.         }
  245.  
  246.         // PENDING(prinz) these should be marked with UIResource if 
  247.         // created by the LAF.
  248.         Caret caret = createCaret();
  249.         editor.setCaret(caret);
  250.         Object o = UIManager.get(prefix + ".caretBlinkRate");
  251.         if ((o != null) && (o instanceof Integer)) {
  252.             Integer rate = (Integer) o;
  253.             caret.setBlinkRate(rate.intValue());
  254.         }
  255.     }
  256.  
  257.     /**
  258.      * Sets the component properties that haven't been explicitly overriden to 
  259.      * null.  A property is considered overridden if its current value
  260.      * is not a UIResource.
  261.      * 
  262.      * @param c the editor component
  263.      * @see #installDefaults
  264.      * @see #uninstallUI
  265.      */
  266.     protected void uninstallDefaults(JComponent c) 
  267.     {
  268.         if (editor.getCaretColor() instanceof UIResource) {
  269.             editor.setCaretColor(null);
  270.         }
  271.                                                                                          
  272.         if (editor.getSelectionColor() instanceof UIResource) {
  273.             editor.setSelectionColor(null);
  274.         }
  275.  
  276.         if (editor.getDisabledTextColor() instanceof UIResource) {
  277.             editor.setDisabledTextColor(null);
  278.         }
  279.  
  280.         if (editor.getSelectedTextColor() instanceof UIResource) {
  281.             editor.setSelectedTextColor(null);
  282.         }
  283.  
  284.         if (editor.getBorder() instanceof UIResource) {
  285.             editor.setBorder(null);
  286.         }
  287.         // PENDING(prinz) these should be marked as a UIResource
  288.         // if installed by the UI.
  289.         editor.setCaret(null);
  290.     }
  291.  
  292.     /**
  293.      * Installs listeners for the UI.
  294.      *
  295.      * @param c the editor component
  296.      */
  297.     protected void installListeners(JComponent c) {
  298.     }
  299.  
  300.     /**
  301.      * Uninstalls listeners for the UI.
  302.      *
  303.      * @param c the editor component
  304.      */
  305.     protected void uninstallListeners(JComponent c) {
  306.     }
  307.  
  308.     /**
  309.      * Paints a background for the view.  This will only be
  310.      * called if isOpaque() on the associated component is
  311.      * true.  The default is to paint the background color 
  312.      * of the component.
  313.      *
  314.      * @param g the graphics context
  315.      */
  316.     protected void paintBackground(Graphics g) {
  317.         g.setColor(editor.getBackground());
  318.         Dimension d = editor.getSize();
  319.         g.fillRect(0, 0, d.width, d.height);
  320.     }
  321.  
  322.     /**
  323.      * Fetches the text component associated with this
  324.      * UI implementation.  This will be null until
  325.      * the ui has been installed.
  326.      *
  327.      * @return the editor component
  328.      */
  329.     protected final JTextComponent getComponent() {
  330.         return editor;
  331.     }
  332.  
  333.     /**
  334.      * Flags model changes.
  335.      * This is called whenever the model has changed.
  336.      * It is implemented to rebuild the view hierarchy
  337.      * to represent the default root element of the
  338.      * associated model.
  339.      */
  340.     protected void modelChanged() {
  341.         // create a view hierarchy
  342.         ViewFactory f = rootView.getViewFactory();
  343.         Document doc = editor.getDocument();
  344.         Element elem = doc.getDefaultRootElement();
  345.         setView(f.create(elem));
  346.     }
  347.  
  348.     /**
  349.      * Sets the current root of the view hierarchy and calls invalidate().
  350.      *
  351.      * @param v the root view
  352.      */
  353.     protected final void setView(View v) {
  354.         rootView.setView(v);
  355.         painted = false;
  356.         editor.invalidate();
  357.     }
  358.  
  359.     /**
  360.      * Paints the interface safely with a guarantee that
  361.      * the model won't change from the view of this thread.  
  362.      * This does the following things, rendering from 
  363.      * back to front.
  364.      * <ol>
  365.      * <li>
  366.      * If the component is marked as opaque, the background
  367.      * is painted in the current background color of the
  368.      * component.
  369.      * <li>
  370.      * The highlights (if any) are painted.
  371.      * <li>
  372.      * The view hierarchy is painted.
  373.      * <li>
  374.      * The caret is painted.
  375.      * </ol>
  376.      *
  377.      * @param g the graphics context
  378.      */
  379.     protected void paintSafely(Graphics g) {
  380.     painted = true;
  381.     Highlighter highlighter = editor.getHighlighter();
  382.     Caret caret = editor.getCaret();
  383.     
  384.     // paint the background
  385.     if (editor.isOpaque()) {
  386.         paintBackground(g);
  387.     }
  388.     
  389.     // paint the highlights
  390.     if (highlighter != null) {
  391.         highlighter.paint(g);
  392.     }
  393.  
  394.     // paint the view hierarchy
  395.     Rectangle alloc = getVisibleEditorRect();
  396.     rootView.paint(g, alloc);
  397.         
  398.     // paint the caret
  399.     if (caret != null) {
  400.         caret.paint(g);
  401.     }
  402.     }
  403.  
  404.     // --- ComponentUI methods --------------------------------------------
  405.  
  406.     /**
  407.      * Installs the UI for a component.  This does the following
  408.      * things.
  409.      * <ol>
  410.      * <li>
  411.      * Set the associated component to opaque (can be changed
  412.      * easily by a subclass or on JTextComponent directly),
  413.      * which is the most common case.  This will cause the
  414.      * component's background color to be painted.
  415.      * <li>
  416.      * Install the default caret and highlighter into the 
  417.      * associated component.
  418.      * <li>
  419.      * Attach to the editor and model.  If there is no 
  420.      * model, a default one is created.
  421.      * <li>
  422.      * create the view factory and the view hierarchy used
  423.      * to represent the model.
  424.      * </ol>
  425.      *
  426.      * @param c the editor component
  427.      * @see ComponentUI#installUI
  428.      */
  429.     public void installUI(JComponent c) {
  430.         if (c instanceof JTextComponent) {
  431.             editor = (JTextComponent) c;
  432.  
  433.             // install defaults
  434.             installDefaults(c);
  435.  
  436.             // common case is background painted... this can
  437.             // easily be changed by subclasses or from outside
  438.             // of the component.
  439.             editor.setOpaque(true);
  440.             editor.setAutoscrolls(true);
  441.  
  442.             editor.setHighlighter(createHighlighter());
  443.  
  444.             // attach to the model and editor
  445.             editor.addPropertyChangeListener(updateHandler);
  446.             Document doc = editor.getDocument();
  447.             if (doc == null) {
  448.                 // no model, create a default one.  This will
  449.                 // fire a notification to the updateHandler 
  450.                 // which takes care of the rest. 
  451.                 editor.setDocument(getEditorKit().createDefaultDocument());
  452.             } else {
  453.                 doc.addDocumentListener(updateHandler);
  454.                 modelChanged();
  455.             }
  456.  
  457.             // install keymap
  458.             installListeners(c);
  459.             editor.setKeymap(createKeymap());       
  460.  
  461.         } else {
  462.             throw new Error("TextUI needs JTextComponent");
  463.         }
  464.     }
  465.  
  466.     /**
  467.      * Deinstalls the UI for a component.  This removes the listeners,
  468.      * uninstalls the highlighter, removes views, and nulls out the keymap.
  469.      *
  470.      * @param c the editor component
  471.      * @see ComponentUI#uninstallUI
  472.      */
  473.     public void uninstallUI(JComponent c) {
  474.         // detach from the model
  475.         editor.removePropertyChangeListener(updateHandler);
  476.         editor.getDocument().removeDocumentListener(updateHandler);
  477.  
  478.         // view part
  479.         painted = false;
  480.         uninstallDefaults(c);
  481.         editor.setHighlighter(null);
  482.         rootView.setView(null);
  483.         c.removeAll();
  484.  
  485.         // controller part
  486.         editor.setKeymap(null);
  487.         uninstallListeners(c);
  488.     }
  489.  
  490.     /**
  491.      * Paints the interface.  This is routed to
  492.      * SafePainter.render() under the guarantee that
  493.      * the model won't change from the view of this thread
  494.      * while it's rendering.  This enables the model to
  495.      * potentially be updated asynchronously.
  496.      *
  497.      * @param g the graphics context
  498.      * @param c the editor component
  499.      */
  500.     public final void paint(Graphics g, JComponent c) {
  501.     if ((rootView.getViewCount() > 0) && (rootView.getView(0) != null)) {
  502.         Runnable painter = new SafePainter(g);
  503.         Document doc = editor.getDocument();
  504.         doc.render(painter);
  505.     }
  506.     }
  507.  
  508.     /**
  509.      * Gets the preferred size for the editor component.  If the component
  510.      * has been given a size prior to receiving this request, it will
  511.      * set the size of the view hierarchy to reflect the size of the component
  512.      * before requesting the preferred size of the view hierarchy.  This
  513.      * allows formatted views to format to the current component size before
  514.      * answering the request.  Other views don't care about currently formatted
  515.      * size and give the same answer either way.
  516.      *
  517.      * @param c the editor component
  518.      * @return the size
  519.      */
  520.     public Dimension getPreferredSize(JComponent c) {
  521.         Insets i = c.getInsets();
  522.     Dimension d = c.getSize();
  523.     if ((d.width > (i.left + i.right)) && (d.height > (i.top + i.bottom))) {
  524.         rootView.setSize(d.width - i.left - i.right, d.height - i.top - i.bottom);
  525.     }
  526.     d.width = (int) Math.min((long) rootView.getPreferredSpan(View.X_AXIS) +
  527.                  (long) i.left + (long) i.right, Integer.MAX_VALUE);
  528.     d.height = (int) Math.min((long) rootView.getPreferredSpan(View.Y_AXIS) +
  529.                   (long) i.top + (long) i.bottom, Integer.MAX_VALUE);
  530.     return d;
  531.     }
  532.  
  533.     /**
  534.      * Gets the minimum size for the editor component.
  535.      *
  536.      * @param c the editor component
  537.      * @return the size
  538.      */
  539.     public Dimension getMinimumSize(JComponent c) {
  540.         Insets i = c.getInsets();
  541.         long width = (rootView.getResizeWeight(View.X_AXIS) > 0) ? 1 : 
  542.             Math.min((long) rootView.getPreferredSpan(View.X_AXIS) + (long) i.left + (long) i.right, Integer.MAX_VALUE);
  543.         long height = (rootView.getResizeWeight(View.Y_AXIS) > 0) ? 1 : 
  544.             Math.min((long) rootView.getPreferredSpan(View.Y_AXIS) + (long) i.top + (long) i.bottom, Integer.MAX_VALUE);
  545.         return new Dimension((int) width, (int) height);
  546.     }
  547.  
  548.     /**
  549.      * Gets the maximum size for the editor component.
  550.      *
  551.      * @param c the editor component
  552.      * @return the size
  553.      */
  554.     public Dimension getMaximumSize(JComponent c) {
  555.         Insets i = c.getInsets();
  556.         long width = (rootView.getResizeWeight(View.X_AXIS) > 0) ? Integer.MAX_VALUE : 
  557.             Math.min((long) rootView.getPreferredSpan(View.X_AXIS) + (long) i.left + (long) i.right, Integer.MAX_VALUE);
  558.         long height = (rootView.getResizeWeight(View.Y_AXIS) > 0) ? Integer.MAX_VALUE : 
  559.             Math.min((long) rootView.getPreferredSpan(View.Y_AXIS) + (long) i.top + (long) i.bottom, Integer.MAX_VALUE);
  560.         return new Dimension((int) width, (int) height);
  561.     }
  562.  
  563.     // ---- TextUI methods -------------------------------------------
  564.  
  565.  
  566.     /**
  567.      * Gets the portion of the editor visibile on the screen.
  568.      *
  569.      * @return the bounding box for the visible portion
  570.      */
  571.     protected Rectangle getVisibleEditorRect() {
  572.         Rectangle alloc = new Rectangle(editor.getSize());
  573.         Insets insets = editor.getInsets();
  574.         alloc.x += insets.left;
  575.         alloc.y += insets.top;
  576.         alloc.width -= insets.left + insets.right;
  577.         alloc.height -= insets.top + insets.bottom;
  578.         return alloc;
  579.     }
  580.  
  581.     /**
  582.      * Converts the given location in the model to a place in
  583.      * the view coordinate system.
  584.      *
  585.      * @param pos the local location in the model to translate >= 0
  586.      * @return the coordinates as a rectangle, null if the model is not painted
  587.      * @exception BadLocationException  if the given position does not
  588.      *   represent a valid location in the associated document
  589.      * @see TextUI#modelToView
  590.      */
  591.     public Rectangle modelToView(int pos) throws BadLocationException {
  592.         if (painted) {
  593.             Rectangle alloc = getVisibleEditorRect();
  594.             Shape s = rootView.modelToView(pos, alloc);
  595.             return s.getBounds();
  596.         }
  597.         return null;
  598.     }
  599.  
  600.     /**
  601.      * Converts the given place in the view coordinate system
  602.      * to the nearest representative location in the model.
  603.      *
  604.      * @param pt the location in the view to translate.  This
  605.      *  should be in the same coordinate system as the mouse events.
  606.      * @return the offset from the start of the document >= 0,
  607.      *   -1 if not painted
  608.      * @see TextUI#viewToModel
  609.      */
  610.     public int viewToModel(Point pt) {
  611.         if (painted) {
  612.             Rectangle alloc = getVisibleEditorRect();
  613.             return rootView.viewToModel(pt.x, pt.y, alloc);
  614.         }
  615.         return -1;
  616.     }
  617.  
  618.     /**
  619.      * Causes the portion of the view responsible for the
  620.      * given part of the model to be repainted.  Does nothing if
  621.      * the view is not currently painted.
  622.      *
  623.      * @param p0 the beginning of the range >= 0
  624.      * @param p1 the end of the range >= p0
  625.      * @see TextUI#damageRange
  626.      */
  627.     public void damageRange(int p0, int p1) {
  628.         if (painted) {
  629.             Rectangle alloc = getVisibleEditorRect();
  630.             try {
  631.                 Shape s0 = rootView.modelToView(p0,alloc);
  632.                 Shape s1 = rootView.modelToView(p1,alloc);
  633.                 if (s0 != null && s1 != null) {
  634.                     Rectangle r0 = s0.getBounds();
  635.                     Rectangle r1 = s1.getBounds();
  636.                     if (r0.y == r1.y) {
  637.                         editor.repaint(r0.x, r0.y, r1.x - r0.x + 1, r0.height);
  638.                     } else {
  639.                         editor.repaint(alloc.x, r0.y, alloc.width, r1.y - r0.y + r1.height);
  640.                     }
  641.                 }
  642.             } catch (BadLocationException e) {}
  643.         }
  644.     }
  645.  
  646.     /**
  647.      * Fetches the EditorKit for the UI.
  648.      *
  649.      * @return the editor capabilities
  650.      * @see TextUI#getEditorKit
  651.      */
  652.     public EditorKit getEditorKit() {
  653.         return defaultKit;
  654.     }
  655.  
  656.     /**
  657.      * Fetches a View with the allocation of the associated 
  658.      * text component (i.e. the root of the hierarchy) that 
  659.      * can be traversed to determine how the model is being
  660.      * represented spatially.
  661.      *
  662.      * @return the view
  663.      * @see TextUI#getRootView
  664.      */
  665.     public View getRootView() {
  666.         return rootView;
  667.     }
  668.  
  669.  
  670.     /**
  671.      * Fetches the default margin space for the text ui, which is
  672.      * Insets(0,0,0,0).
  673.      *
  674.      * @return the margins
  675.      */
  676.     public Insets getDefaultMargin() {
  677.         return new Insets(0,0,0,0);
  678.     }
  679.  
  680.  
  681.     // --- ViewFactory methods ------------------------------
  682.  
  683.     /**
  684.      * Creates a view for an element.
  685.      * If a subclass wishes to directly implement the factory
  686.      * producing the view(s), it should reimplement this 
  687.      * method.  By default it simply returns null indicating
  688.      * it is unable to represent the element.
  689.      *
  690.      * @param elem the element
  691.      * @return the view
  692.      */
  693.     public View create(Element elem) {
  694.         return null;
  695.     }
  696.  
  697.     /**
  698.      * Creates a view for an element.
  699.      * If a subclass wishes to directly implement the factory
  700.      * producing the view(s), it should reimplement this 
  701.      * method.  By default it simply returns null indicating
  702.      * it is unable to represent the part of the element.
  703.      *
  704.      * @param elem the element
  705.      * @param p0 the starting offset >= 0
  706.      * @param p1 the ending offset >= p0
  707.      * @return the view
  708.      */
  709.     public View create(Element elem, int p0, int p1) {
  710.         return null;
  711.     }
  712.  
  713.     // --- serialization ---------------------------------------------
  714.  
  715.     private void readObject(ObjectInputStream s)
  716.       throws ClassNotFoundException, IOException 
  717.     {
  718.         s.defaultReadObject();
  719.         rootView = new RootView();
  720.         updateHandler = new UpdateHandler();
  721.     }
  722.  
  723.     // ----- member variables ---------------------------------------
  724.  
  725.     private static final EditorKit defaultKit = new DefaultEditorKit();
  726.     transient JTextComponent editor;
  727.     transient boolean painted;
  728.     transient RootView rootView = new RootView();
  729.     transient UpdateHandler updateHandler = new UpdateHandler();
  730.  
  731.     /**
  732.      * Root view that acts as a gateway between the component
  733.      * and the View hierarchy.
  734.      */
  735.     class RootView extends View {
  736.  
  737.         RootView() {
  738.             super(null);
  739.         }
  740.  
  741.         void setView(View v) {
  742.             if (view != null) {
  743.                 // get rid of back reference so that the old
  744.                 // hierarchy can be garbage collected.
  745.                 view.setParent(null);
  746.             }
  747.             view = v;
  748.             if (view != null) {
  749.                 view.setParent(this);
  750.             }
  751.         }
  752.  
  753.         /**
  754.          * Determines the preferred span for this view along an axis.
  755.          *
  756.          * @param axis may be either X_AXIS or Y_AXIS
  757.          * @return the span the view would like to be rendered into.
  758.          *         Typically the view is told to render into the span
  759.          *         that is returned, although there is no guarantee.
  760.          *         The parent may choose to resize or break the view.
  761.          */
  762.         public float getPreferredSpan(int axis) {
  763.             if (view != null) {
  764.                 return view.getPreferredSpan(axis);
  765.             }
  766.             return 10;
  767.         }
  768.  
  769.         /**
  770.          * Specifies that a preference has changed.
  771.          * Child views can call this on the parent to indicate that
  772.          * the preference has changed.  The root view routes this to
  773.          * invalidate on the hosting component.
  774.          * <p>
  775.          * This can be called on a different thread from the
  776.          * event dispatching thread and is basically unsafe to
  777.          * propagate into the component.  To make this safe,
  778.          * the operation is transferred over to the event dispatching 
  779.          * thread for completion.  It is a design goal that all view
  780.          * methods be safe to call without concern for concurrency,
  781.          * and this behavior helps make that true.
  782.          *
  783.          * @param child the child view
  784.          * @param width true if the width preference has changed
  785.          * @param height true if the height preference has changed
  786.          */ 
  787.         public void preferenceChanged(View child, boolean width, boolean height) {
  788.             editor.revalidate();
  789.         }
  790.  
  791.         /**
  792.          * Determines the desired alignment for this view along an axis.
  793.          *
  794.          * @param axis may be either X_AXIS or Y_AXIS
  795.          * @return the desired alignment, where 0.0 indicates the origin
  796.          *     and 1.0 the full span away from the origin
  797.          */
  798.         public float getAlignment(int axis) {
  799.             if (view != null) {
  800.                 return view.getAlignment(axis);
  801.             }
  802.             return 0;
  803.         }
  804.  
  805.         /**
  806.          * Renders the view.
  807.          *
  808.          * @param g the graphics context
  809.          * @param allocation the region to render into
  810.          */
  811.         public void paint(Graphics g, Shape allocation) {
  812.             if (view != null) {
  813.                 Rectangle alloc = allocation.getBounds();
  814.                 view.setSize(alloc.width, alloc.height);
  815.                 view.paint(g, allocation);
  816.             }
  817.         }
  818.         
  819.         /**
  820.          * Sets the view parent.
  821.          *
  822.          * @param parent the parent view
  823.          */
  824.         public void setParent(View parent) {
  825.             throw new Error("Can't set parent on root view");
  826.         }
  827.  
  828.         /** 
  829.          * Returns the number of views in this view.  Since
  830.          * this view simply wraps the root of the view hierarchy
  831.          * it has exactly one child.
  832.          *
  833.          * @return the number of views
  834.          * @see #getView
  835.          */
  836.         public int getViewCount() {
  837.             return 1;
  838.         }
  839.  
  840.         /** 
  841.          * Gets the n-th view in this container.
  842.          *
  843.          * @param n the number of the view to get
  844.          * @return the view
  845.          */
  846.         public View getView(int n) {
  847.             return view;
  848.         }
  849.  
  850.         /**
  851.          * Fetches the allocation for the given child view. 
  852.          * This enables finding out where various views
  853.          * are located, without assuming the views store
  854.          * their location.  This returns the given allocation
  855.          * since this view simply acts as a gateway between
  856.          * the view hierarchy and the associated component.
  857.          *
  858.          * @param index the index of the child
  859.          * @param a  the allocation to this view.
  860.          * @return the allocation to the child
  861.          */
  862.         public Shape getChildAllocation(int index, Shape a) {
  863.             return a;
  864.         }
  865.  
  866.         /**
  867.          * Provides a mapping from the document model coordinate space
  868.          * to the coordinate space of the view mapped to it.
  869.          *
  870.          * @param pos the position to convert
  871.          * @param a the allocated region to render into
  872.          * @return the bounding box of the given position
  873.          */
  874.         public Shape modelToView(int pos, Shape a) throws BadLocationException {
  875.             if (view != null) {
  876.                 return view.modelToView(pos, a);
  877.             }
  878.             return null;
  879.         }
  880.  
  881.         /**
  882.          * Provides a mapping from the view coordinate space to the logical
  883.          * coordinate space of the model.
  884.          *
  885.          * @param x x coordinate of the view location to convert
  886.          * @param y y coordinate of the view location to convert
  887.          * @param a the allocated region to render into
  888.          * @return the location within the model that best represents the
  889.          *    given point in the view
  890.          */
  891.         public int viewToModel(float x, float y, Shape a) {
  892.             if (view != null) {
  893.                 return view.viewToModel(x, y, a);
  894.             }
  895.             return -1;
  896.         }
  897.  
  898.         /**
  899.          * Gives notification that something was inserted into the document
  900.          * in a location that this view is responsible for.
  901.          *
  902.          * @param e the change information from the associated document
  903.          * @param a the current allocation of the view
  904.          * @param f the factory to use to rebuild if the view has children
  905.          */
  906.         public void insertUpdate(DocumentEvent e, Shape a, ViewFactory f) {
  907.             if (view != null) {
  908.                 view.insertUpdate(e, a, f);
  909.             }
  910.         }
  911.         
  912.         /**
  913.          * Gives notification that something was removed from the document
  914.          * in a location that this view is responsible for.
  915.          *
  916.          * @param e the change information from the associated document
  917.          * @param a the current allocation of the view
  918.          * @param f the factory to use to rebuild if the view has children
  919.          */
  920.         public void removeUpdate(DocumentEvent e, Shape a, ViewFactory f) {
  921.             if (view != null) {
  922.                 view.removeUpdate(e, a, f);
  923.             }
  924.         }
  925.  
  926.         /**
  927.          * Gives notification from the document that attributes were changed
  928.          * in a location that this view is responsible for.
  929.          *
  930.          * @param e the change information from the associated document
  931.          * @param a the current allocation of the view
  932.          * @param f the factory to use to rebuild if the view has children
  933.          */
  934.         public void changedUpdate(DocumentEvent e, Shape a, ViewFactory f) {
  935.             if (view != null) {
  936.                 view.changedUpdate(e, a, f);
  937.             }
  938.         }
  939.  
  940.         /**
  941.          * Returns the document model underlying the view.
  942.          *
  943.          * @return the model
  944.          */
  945.         public Document getDocument() {
  946.             return editor.getDocument();
  947.         }
  948.         
  949.         /**
  950.          * Returns the starting offset into the model for this view.
  951.          *
  952.          * @return the starting offset
  953.          */
  954.         public int getStartOffset() {
  955.             if (view != null) {
  956.                 return view.getStartOffset();
  957.             }
  958.             return getElement().getStartOffset();
  959.         }
  960.  
  961.         /**
  962.          * Returns the ending offset into the model for this view.
  963.          *
  964.          * @return the ending offset
  965.          */
  966.         public int getEndOffset() {
  967.             if (view != null) {
  968.                 return view.getEndOffset();
  969.             }
  970.             return getElement().getEndOffset();
  971.         }
  972.  
  973.         /**
  974.          * Gets the element that this view is mapped to.
  975.          *
  976.          * @return the view
  977.          */
  978.         public Element getElement() {
  979.             if (view != null) {
  980.                 return view.getElement();
  981.             }
  982.             return editor.getDocument().getDefaultRootElement();
  983.         }
  984.  
  985.         /**
  986.          * Breaks this view on the given axis at the given length.
  987.          *
  988.          * @param axis may be either X_AXIS or Y_AXIS
  989.          * @param len specifies where a break is desired in the span
  990.          * @param the current allocation of the view
  991.          * @return the fragment of the view that represents the given span
  992.          *   if the view can be broken, otherwise null
  993.          */
  994.         public View breakView(int axis, float len, Shape a) {
  995.             throw new Error("Can't break root view");
  996.         }
  997.  
  998.         /**
  999.          * Determines the resizability of the view along the
  1000.          * given axis.  A value of 0 or less is not resizable.
  1001.          *
  1002.          * @param axis may be either X_AXIS or Y_AXIS
  1003.          * @return the weight
  1004.          */
  1005.         public int getResizeWeight(int axis) {
  1006.             if (view != null) {
  1007.                 return view.getResizeWeight(axis);
  1008.             }
  1009.             return 0;
  1010.         }
  1011.  
  1012.         /**
  1013.          * Sets the view size.
  1014.          *
  1015.          * @param width the width
  1016.          * @param height the height
  1017.          */
  1018.         public void setSize(float width, float height) {
  1019.             if (view != null) {
  1020.                 view.setSize(width, height);
  1021.             }
  1022.         }
  1023.  
  1024.         /**
  1025.          * Fetches the container hosting the view.  This is useful for
  1026.          * things like scheduling a repaint, finding out the host 
  1027.          * components font, etc.  The default implementation
  1028.          * of this is to forward the query to the parent view.
  1029.          *
  1030.          * @return the container
  1031.          */
  1032.         public Container getContainer() {
  1033.             return editor;
  1034.         }
  1035.         
  1036.         /**
  1037.          * Fetches the factory to be used for building the
  1038.          * various view fragments that make up the view that
  1039.          * represents the model.  This is what determines
  1040.          * how the model will be represented.  This is implemented
  1041.          * to fetch the factory provided by the associated
  1042.          * EditorKit unless that is null, in which case this
  1043.          * simply returns the BasicTextUI itself which allows
  1044.          * subclasses to implement a simple factory directly without
  1045.          * creating extra objects.  
  1046.          *
  1047.          * @return the factory
  1048.          */
  1049.         public ViewFactory getViewFactory() {
  1050.             EditorKit kit = getEditorKit();
  1051.             ViewFactory f = kit.getViewFactory();
  1052.             if (f != null) {
  1053.                 return f;
  1054.             }
  1055.             return BasicTextUI.this;
  1056.         }
  1057.  
  1058.         private View view;
  1059.  
  1060.     }
  1061.  
  1062.     class SafePainter implements Runnable {
  1063.         
  1064.         SafePainter(Graphics g) {
  1065.             this.g = g;
  1066.         }
  1067.  
  1068.         /**
  1069.          * Render the UI.  This will be called by the 
  1070.          * associated model.
  1071.          */
  1072.         public void run() {
  1073.             paintSafely(g);
  1074.         }
  1075.  
  1076.         Graphics g;
  1077.     }
  1078.  
  1079.     /**
  1080.      * Handles updates from various places.  If the model is changed,
  1081.      * this class unregisters as a listener to the old model and 
  1082.      * registers with the new model.  If the document model changes,
  1083.      * the change is forwarded to the root view.  If the focus
  1084.      * accelerator changes, a new keystroke is registered to request
  1085.      * focus.
  1086.      */
  1087.     class UpdateHandler implements PropertyChangeListener, DocumentListener {
  1088.  
  1089.         // --- PropertyChangeListener methods -----------------------
  1090.  
  1091.         /**
  1092.          * This method gets called when a bound property is changed.
  1093.          * We are looking for document changes on the editor.
  1094.          */
  1095.         public final void propertyChange(PropertyChangeEvent evt) {
  1096.             Object oldValue = evt.getOldValue();
  1097.             Object newValue = evt.getNewValue();
  1098.             if ((oldValue instanceof Document) || (newValue instanceof Document)) {
  1099.                 if (oldValue != null) {
  1100.                     ((Document)oldValue).removeDocumentListener(this);
  1101.                 }
  1102.                 if (newValue != null) {
  1103.                     ((Document)newValue).addDocumentListener(this);
  1104.                 }
  1105.                 modelChanged();
  1106.             }
  1107.             BasicTextUI.this.propertyChange(evt);
  1108.         }
  1109.  
  1110.         // --- DocumentListener methods -----------------------
  1111.  
  1112.         /**
  1113.          * The insert notification.  Gets sent to the root of the view structure
  1114.          * that represents the portion of the model being represented by the
  1115.          * editor.  The factory is added as an argument to the update so that
  1116.          * the views can update themselves in a dynamic (not hardcoded) way.
  1117.          *
  1118.          * @param e  The change notification from the currently associated
  1119.          *  document.
  1120.          * @see DocumentListener#insertUpdate
  1121.          */
  1122.         public final void insertUpdate(DocumentEvent e) {
  1123.             Rectangle alloc = (painted) ? new Rectangle(editor.getSize()) : null;
  1124.             rootView.insertUpdate(e, alloc, rootView.getViewFactory());
  1125.         }
  1126.  
  1127.         /**
  1128.          * The remove notification.  Gets sent to the root of the view structure
  1129.          * that represents the portion of the model being represented by the
  1130.          * editor.  The factory is added as an argument to the update so that
  1131.          * the views can update themselves in a dynamic (not hardcoded) way.
  1132.          *
  1133.          * @param e  The change notification from the currently associated
  1134.          *  document.
  1135.          * @see DocumentListener#removeUpdate
  1136.          */
  1137.         public final void removeUpdate(DocumentEvent e) {
  1138.             Rectangle alloc = (painted) ? new Rectangle(editor.getSize()) : null;
  1139.             rootView.removeUpdate(e, alloc, rootView.getViewFactory());
  1140.         }
  1141.  
  1142.         /**
  1143.          * The change notification.  Gets sent to the root of the view structure
  1144.          * that represents the portion of the model being represented by the
  1145.          * editor.  The factory is added as an argument to the update so that
  1146.          * the views can update themselves in a dynamic (not hardcoded) way.
  1147.          *
  1148.          * @param e  The change notification from the currently associated
  1149.          *  document.
  1150.          * @see DocumentListener#changeUpdate
  1151.          */
  1152.         public final void changedUpdate(DocumentEvent e) {
  1153.             Rectangle alloc = (painted) ? new Rectangle(editor.getSize()) : null;
  1154.             rootView.changedUpdate(e, alloc, rootView.getViewFactory());
  1155.         }
  1156.     }
  1157.  
  1158. }
  1159.  
  1160.